﻿using System;
using System.Linq;
using AbstractBugTracker;
using Jira.JiraSoap;
using log4net;

namespace Jira
{
    public sealed class JiraBug : IBug
    {
        private static readonly ILog s_Log = LogManager.GetLogger("Jira.JiraBug");

        private readonly JiraConfig m_JiraConfig;
        private readonly JiraConnection m_Connection;
        private readonly RemoteIssue m_Issue;
        
        internal JiraBug(JiraConfig jiraConfig, JiraConnection connection, RemoteIssue issue)
        {
            m_JiraConfig = jiraConfig;
            m_Connection = connection;
            m_Issue = issue;
        }


        public Uri Uri
        {
            get { return new Uri(new Uri(m_Connection.Client.Endpoint.Address.Uri, "../../browse/"), m_Issue.key); }
        }

        public void AddComment(string body, bool isJson)
        {
            RemoteComment comment = new RemoteComment
                                        {
                                            body = JiraUtils.Clean(body, isJson)
                                        };
            m_Connection.Client.addComment(m_Connection.Token, m_Issue.key, comment);
            s_Log.InfoFormat("Commented on {0}", m_Issue.key);
        }

        public string Description
        {
            get { return m_Issue.description; }
        }

        public BugState State
        {
            get
            {
                string statusName = m_Connection.GetStatusName(m_Issue.status);
                if (m_JiraConfig.ResolvedStatusNames.Contains(statusName))
                {
                    string resolutionName = m_Connection.GetResolutionName(m_Issue.resolution);
                    if (m_JiraConfig.FixedResolutionNames.Contains(resolutionName))
                    {
                        return BugState.BelievedFixed;
                    }
                }

                return BugState.Unfixed;
            }
        }

        public void Close()
        {
            ProgressWorkflowAction(m_JiraConfig.CloseActionName, new RemoteFieldValue { id = "resolution", values = new[] { m_Connection.GetResolutionId(m_JiraConfig.CloseResolutionName) } });
        }

        public void Reopen()
        {
            ProgressWorkflowAction(m_JiraConfig.ReopenActionName);
        }

        private void ProgressWorkflowAction(string workflowAction, params RemoteFieldValue[] fieldsToUpdate)
        {
            string workflowActionId = m_Connection.GetActionId(m_Issue, workflowAction);
            m_Connection.Client.progressWorkflowAction(m_Connection.Token, m_Issue.key, workflowActionId, fieldsToUpdate);
            s_Log.InfoFormat("Completed {0} {1}", workflowAction, m_Issue.key);
        }

        public DateTime ResolvedDateUtc
        {
            get
            {
                return m_Connection.Client.getResolutionDateByKey(m_Connection.Token, m_Issue.key);
            }
        }

        public long ReportCount
        {
            get
            {
                string reportCount = GetCustomField(m_JiraConfig.ReportCountFieldName);
                return ParseLongOrZero(reportCount);
            }
            set
            {
                SetCustomField(m_JiraConfig.ReportCountFieldName, value.ToString());
            }
        }

        public string AutomaticEmail
        {
            get
            {
                return GetCustomField(m_JiraConfig.AutomaticEmailFieldName);
            }
        }

        public void CommitChanges()
        {
            // Nothing to do, we have committed all changes eagerly
        }

        private static long ParseLongOrZero(string reportCount)
        {
            long result;
            if (reportCount == null)
            {
                result = 0;
            }
            else
            {
                Int64.TryParse(reportCount, out result);
            }
            return result;
        }

        public Version BuildFixedOn
        {
            get
            {
                string version = GetCustomField(m_JiraConfig.BuildFixedOnFieldName);
                return ParseVersionOrNull(version);
            }
        }

        private static Version ParseVersionOrNull(string version)
        {
            try
            {
                return version == null ? null : new Version(version);
            }
            catch (ArgumentException)
            {
                return null;
            }
            catch (FormatException)
            {
                return null;
            }
            catch (OverflowException)
            {
                return null;
            }
        }

        public Version EarliestBuild
        {
            get
            {
                string version = GetCustomField(m_JiraConfig.EarliestBuildSeenFieldName);
                return ParseVersionOrNull(version);
            }
            set
            {
                SetCustomField(m_JiraConfig.EarliestBuildSeenFieldName, value.ToString());
            }
        }

        public Version LatestBuild
        {
            get
            {
                string version = GetCustomField(m_JiraConfig.LatestBuildSeenFieldName);
                return ParseVersionOrNull(version);
            }
            set
            {
                SetCustomField(m_JiraConfig.LatestBuildSeenFieldName, value.ToString());
            }
        }

        public string GetCustomField(string name)
        {
            return JiraUtils.GetCustomField(m_Issue, m_Connection.GetCustomFieldId(m_Issue, name));
        }

        public void SetCustomField(string name, string value)
        {
            RemoteFieldValue remoteFieldValue = new RemoteFieldValue
            {
                id = m_Connection.GetCustomFieldId(m_Issue, name),
                values = new[] { JiraUtils.Clean(value, false) }
            };
            m_Connection.Client.updateIssue(m_Connection.Token, m_Issue.key, new[] { remoteFieldValue });
        }

        public string Key
        {
            get { return m_Issue.key; }
        }

        public override string ToString()
        {
            return m_Issue.key;
        }
    }
}
